/***************************************************************************
 *   Copyright (C) 2009 - 2010 by Simon Qian <SimonQian@SimonQian.com>     *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

#include "vsf.h"

#define STM32_FLASH_BASEADDR			0x08000000
#define STM32_FLASH_ADDR(addr)			(STM32_FLASH_BASEADDR + (addr))
#define STM32_FLASH_SIZE_KB				(*(uint16_t *)0x1FFFF7E0)

#define STM32_FLASH_KEYR_KEY1			(uint32_t)0x45670123
#define STM32_FLASH_KEYR_KEY2			(uint32_t)0xCDEF89AB
#define STM32_FLASH_OPTKEYR_KEY1		(uint32_t)0x45670123
#define STM32_FLASH_OPTKEYR_KEY2		(uint32_t)0xCDEF89AB

#define VSFHAL_FLASH_NUM					1

struct vsfhal_flash_t
{
	struct
	{
		void *param;
		void (*onfinish)(void*, vsf_err_t);
	} cb;
	uint8_t bank;
} static vsfhal_flash[VSFHAL_FLASH_NUM];

static vsf_err_t vsfhal_flash_checkidx(uint8_t index)
{
	return (index < VSFHAL_FLASH_NUM) ? VSFERR_NONE : VSFERR_NOT_SUPPORT;
}

vsf_err_t vsfhal_flash_capacity(uint8_t index, uint32_t *pagesize, 
									uint32_t *pagenum)
{
	uint16_t flash_size, page_size;

	switch (index)
	{
	case 0:
		flash_size = STM32_FLASH_SIZE_KB;
		page_size = (flash_size >= 256) ? 1 : 0;
		if (NULL != pagesize)
			*pagesize = 1024 << page_size;
		if (NULL != pagenum)
			*pagenum = flash_size >> page_size;
		return VSFERR_NONE;
	default:
		return VSFERR_NOT_SUPPORT;
	}
}

uint32_t vsfhal_flash_baseaddr(uint8_t index)
{
	return STM32_FLASH_BASEADDR;
}

uint32_t vsfhal_flash_blocksize(uint8_t index, uint32_t addr, uint32_t size,
		int op)
{
	uint32_t pagesize;
	if (vsfhal_flash_capacity(index, &pagesize, NULL))
		return 0;
	return !op ? pagesize :
#ifdef GD32F
		4
#else
		2
#endif
		;
}

vsf_err_t vsfhal_flash_config_cb(uint8_t index, uint32_t int_priority,
		void *param, void (*onfinish)(void*, vsf_err_t))
{
	if (vsfhal_flash_checkidx(index))
		return VSFERR_NOT_SUPPORT;

	vsfhal_flash[index].cb.param = param;
	vsfhal_flash[index].cb.onfinish = onfinish;
	if (onfinish != NULL)
	{
		NVIC_SetPriority(FLASH_IRQn, int_priority);
		NVIC_EnableIRQ(FLASH_IRQn);
	}
	else
	{
		NVIC_DisableIRQ(FLASH_IRQn);
	}
	return VSFERR_NONE;
}

vsf_err_t vsfhal_flash_init(uint8_t index)
{
	FLASH->KEYR = STM32_FLASH_KEYR_KEY1;
	FLASH->KEYR = STM32_FLASH_KEYR_KEY2;

#ifdef STM32F10X_XL
	FLASH->KEYR2 = STM32_FLASH_KEYR_KEY1;
	FLASH->KEYR2 = STM32_FLASH_KEYR_KEY2;
#endif

	memset(vsfhal_flash, 0, sizeof(struct vsfhal_flash_t));
	return VSFERR_NONE;
}

vsf_err_t vsfhal_flash_fini(uint8_t index)
{
	if (vsfhal_flash_checkidx(index))
		return VSFERR_NOT_SUPPORT;

	FLASH->CR |= FLASH_CR_LOCK;
#ifdef STM32F10X_XL
	FLASH->CR2 = FLASH_CR_LOCK;
#endif
	return VSFERR_NONE;
}

vsf_err_t vsfhal_flash_erase(uint8_t index, uint32_t addr)
{
	volatile uint32_t *CR, *AR, *SR;

	switch (index)
	{
	case 0:
#ifdef STM32F10X_XL
		if (addr < 512 * 1024)
		{
#endif
			vsfhal_flash[0].bank = 0;
			CR = &FLASH->CR;
			AR = &FLASH->AR;
			SR = &FLASH->SR;
#ifdef STM32F10X_XL
		}
		else
		{
			vsfhal_flash[0].bank = 1;
			CR = &FLASH->CR2;
			AR = &FLASH->AR2;
			SR = &FLASH->SR2;
		}
#endif

		if (*CR & FLASH_SR_BSY)
			return VSFERR_NOT_READY;

		*CR |= FLASH_CR_PER | FLASH_CR_EOPIE;
		*AR = STM32_FLASH_ADDR(addr);
		*CR |= FLASH_CR_STRT;
		break;
	default:
		return VSFERR_NOT_SUPPORT;
	}

	if (!vsfhal_flash[index].cb.onfinish)
	{
		while (*SR & FLASH_SR_BSY);
		*CR &= ~(FLASH_CR_PER | FLASH_CR_EOPIE);
		*SR = FLASH_SR_BSY | FLASH_SR_PGERR | FLASH_SR_WRPRTERR | FLASH_SR_EOP;
	}
	return VSFERR_NONE;
}

vsf_err_t vsfhal_flash_read(uint8_t index, uint32_t addr, uint8_t *buff)
{
	return VSFERR_NOT_SUPPORT;
}

vsf_err_t vsfhal_flash_write(uint8_t index, uint32_t addr, uint8_t *buff)
{
	volatile uint32_t *CR, *SR;

	switch (index)
	{
	case 0:
#ifdef STM32F10X_XL
		if (addr < 512 * 1024)
		{
#endif
			vsfhal_flash[0].bank = 0;
			CR = &FLASH->CR;
			SR = &FLASH->SR;
#ifdef STM32F10X_XL
		}
		else
		{
			vsfhal_flash[0].bank = 1;
			CR = &FLASH->CR2;
			SR = &FLASH->SR2;
		}
#endif

#ifdef GD32F
		// for GD32, original content MUST be 0xFFFFFFFF
		if (*(uint32_t *)STM32_FLASH_ADDR(addr) != 0xFFFFFFFF)
			return VSFERR_FAIL;
#endif

		*CR |= FLASH_CR_PG | FLASH_CR_EOPIE;
#ifdef GD32F
		*(uint32_t *)STM32_FLASH_ADDR(addr) = *(uint32_t *)buff;
#else
		*(uint16_t *)STM32_FLASH_ADDR(addr) = *(uint16_t *)buff;
#endif
		break;
	default:
		return VSFERR_NOT_SUPPORT;
	}

	if (!vsfhal_flash[index].cb.onfinish)
	{
		while (*SR & FLASH_SR_BSY);
		*CR &= ~(FLASH_CR_PG | FLASH_CR_EOPIE);
		*SR = FLASH_SR_BSY | FLASH_SR_PGERR | FLASH_SR_WRPRTERR | FLASH_SR_EOP;
	}
	return VSFERR_NONE;
}

ROOTFUNC void FLASH_IRQHandler(void)
{
	volatile uint32_t *CR, *SR;

#ifdef STM32F10X_XL
	if (!stm32_flash[0].bank)
	{
#endif
		CR = &FLASH->CR;
		SR = &FLASH->SR;
#ifdef STM32F10X_XL
	}
	else
	{
		CR = &FLASH->CR2;
		SR = &FLASH->SR2;
	}
#endif

	*CR &= ~(FLASH_CR_PER | FLASH_CR_PG | FLASH_CR_EOPIE);
	if (vsfhal_flash[0].cb.onfinish != NULL)
	{
		vsfhal_flash[0].cb.onfinish(vsfhal_flash[0].cb.param,
			(*SR & (FLASH_SR_WRPRTERR | FLASH_SR_PGERR)) ? VSFERR_FAIL : VSFERR_NONE);
	}
	*SR = FLASH_SR_BSY | FLASH_SR_PGERR | FLASH_SR_WRPRTERR | FLASH_SR_EOP;
}
